/**
 * @author spider
 * This class is executed in the remote machines. It stores the data models
 * and controls methods necessary to get the ball rolling.
 */
package br.unb.core.dispatcher;

import java.io.IOException;
import java.net.UnknownHostException;

import br.unb.core.message.Message;
import br.unb.core.synchronism.Synchronism;
import br.unb.core.traffic.Traffic;
import br.unb.utils.Console;
import br.unb.utils.ConsoleInterface;
import br.unb.utils.Resource;
import javax.swing.text.DefaultStyledDocument;

public class Dispatcher implements Runnable, ConsoleInterface {

    /** Socket for receiving data*/
    private static final DispatcherSocket DISPACHER_SOCKET = DispatcherSocket.getInstance();
    /** Sender list, responds to controller machine*/
    private static final TrafficDispatcher TRAFFIC_DISPATCHER = new TrafficDispatcher();
    /** Sync dispatcher, for synchronization*/
    private static final SyncDispatcher SYNC_DISPATCHER = new SyncDispatcher();
    /**Singletron instance*/
    private static final Dispatcher INSTANCE = new Dispatcher(); //no lazy initialization!

    public static Dispatcher getInstance() {
        return INSTANCE;
    }

    private Dispatcher() {
        new Thread(this, "Dispatcher").start();
    }

    /********************* Main loop on target machines ************************/
    /**
     * (EXECUTED ON RECEIVER/SENDER/SERVER/CLIENT MACHINE !!!)
     * Waits for a control message from the network
     * Does the apropriate action depending of the machine its running on.
     */
    public void run() {
        while (true) {
            try {
                Message request = DISPACHER_SOCKET.receive();
                Message response = null;
                switch (request.getType()) {
                    case PREPARE_TO_RECEIVE: {
                        response = TRAFFIC_DISPATCHER.prepareToReceive((Traffic) request.getTraffic());
                    }
                    break;
                    case STOP_RECEIVER: {
                        response = TRAFFIC_DISPATCHER.stopReceiver((String) request.getTrafficID());
                    }
                    break;
                    case DELETE_RECEIVER: {
                        response = TRAFFIC_DISPATCHER.deleteReceiver((String) request.getTrafficID());
                    }
                    break;
                    case PREPARE_TO_SEND: {
                        response = TRAFFIC_DISPATCHER.prepareToSend((Traffic) request.getTraffic());
                    }
                    break;
                    case START_SENDER: {
                        response = TRAFFIC_DISPATCHER.startSender((String) request.getTrafficID());
                    }
                    break;
                    case STOP_SENDER: {
                        response = TRAFFIC_DISPATCHER.stopSender((String) request.getTrafficID());
                    }
                    break;
                    case DELETE_SENDER: {
                        response = TRAFFIC_DISPATCHER.deleteSender((String) request.getTrafficID());
                    }
                    break;
                    case START_SYNCCLIENT: {
                        response = SYNC_DISPATCHER.startSyncClient((Synchronism) request.getSyncModel());
                    }
                    break;
                    case STOP_SYNCCLIENT: {
                        response = SYNC_DISPATCHER.stopClientSync();
                    }
                    break;
                    case START_SYNCSERVER: {
                        response = SYNC_DISPATCHER.startSyncServer((Synchronism) request.getSyncModel());
                    }
                    break;
                    case STOP_SYNCSERVER: {
                        response = SYNC_DISPATCHER.stopServerSync();
                    }
                    break;
                    /*
                    case OK_SYNC: {
                    //* Received on Controller machine
                    Manager.getInstance().okSync((Synchronism) request.getSyncModel());
                    }
                    break;*/
                    default: {
                        printError(Integer.toString(request.getType().ordinal()));
                        printError(
                                Resource.getString("DISPACHER SAYS:")
                                + Resource.getString("NOT_FOR_ME")
                                + Resource.getString("UNKNOW")
                                + Resource.getString(request.getType().name()));

                    }
                    break;
                }
                if (response != null) {
                    DISPACHER_SOCKET.answer(response);
                }
            } catch (IOException ioe) {
                Console.println(
                        Resource.getString("ERROR_RECEIVING_CONTROL_MESSAGE") + ioe.getMessage(), System.err, doc);
            }
        }
    }

    public void sendSyncOk(Synchronism syncModel) throws UnknownHostException {
        //The only place a sync_ok message can be generated!
        /**
         * (Executed on clientController thread)
         * Response at the end of the synchronization
         */
        DISPACHER_SOCKET.sendDispacher(
                new Message(Message.Type.OK_SYNC, syncModel),
                syncModel.getSyncManagerIpAddress());
    }

    public void sendSyncFail(Synchronism syncModel) throws UnknownHostException {
        /**
         * (Executed on clientController thread)
         * Response at the end of the synchronization
         */
        DISPACHER_SOCKET.sendDispacher(
                new Message(Message.Type.ERROR, Resource.getString("CLIENT_COULD_NOT_FIND_SERVER")),
                syncModel.getSyncManagerIpAddress());

    }

    /**
     * (Executed on SenderController thread)
     * Response at the end of traffic transmission
     */
    public void sendSenderFinishedAck(Traffic t) {
        DISPACHER_SOCKET.sendStatus(
                new Message(Message.Type.ACK,
                Resource.getString("SENDER_FINISHED") + t.getName()));
        TrafficDispatcher.sendersMap.remove(t);
    }

    /**
     * (Executed on ReceiverController thread)
     * Response at the end of the synchronization
     */
    public void sendReceiverFinishedAck(Traffic t) {
        DISPACHER_SOCKET.sendStatus(
                new Message(Message.Type.ACK,
                Resource.getString("RECEIVER_FINISHED") + t.getName()));
        TrafficDispatcher.receiversMap.remove(t);
    }

    /*ConsoleInterface methods*/
    public static DefaultStyledDocument doc = null;

    @Override
    public void printError(String s) {
        Console.println(s, System.err, doc);
    }

    @Override
    public void println(String s) {
        Console.println(s, System.out, doc);
    }

    @Override
    public void setDocument(DefaultStyledDocument doc) {
        Dispatcher.doc = doc;
    }
}
